

package generation;
import java.util.ArrayList;

import astnode.ASTNode;
import astnode.AssignStmtNode;
import astnode.BinaryOperationNode;
import astnode.BranchStmtNode;
import astnode.CallNode;
import astnode.CaseNode;
import astnode.CompoundStmtNode;
import astnode.DeclarationNode;
import astnode.ExpressionNode;
import astnode.FunctionDeclarationNode;
import astnode.IfStmtNode;
import astnode.LiteralNode;
import astnode.LoopStmtNode;
import astnode.MarkerStmtNode;
import astnode.ParameterNode;
import astnode.ReturnStmtNode;
import astnode.StmtNode;
import astnode.UnitaryOperationNode;
import astnode.VariableDeclarationNode;
import astnode.VariableNode;

import common.LogPrinter;
import common.TokenType;
import common.Visitor;

/**
 * @author Yunke Li, Boshi Li
 */
public class GenerationVisitor implements Visitor {	
	
	private LogPrinter logPrinter = null;				//logPrinter for printing log
	private ArrayList<String> spellingTable = null;		//spelling table for getting lexeme via index
	private CodeTable codeTable = null;					//buffer for generated code
	private int level = 0;											//nesting level
	private static int temp = 0;								//counter for temporary value
	private Recorder displacement = new Recorder();		//we need a class since one group nodes share it, and other group share another	
	private static int lab = 0;							//for mallocing lab, it is static since all nodes in the AST shares lab	
	private int funSize = 0;								//for generate function codes
	private String loopBeginLab = "";				//for generate loop codes
	private String loopEndLab = "";				//for generate loop codes	
	private String switchDefaultLab = "";		//for generate switch codes
	private String switchItem = "";					//for generate switch codes
	private static String switchCurrentLab = "";		//it is static since all case nodes share this flag, one need to set it, and another one need to get it.
	private String switchEndLab = "";				//for generate switch codes
	private boolean needLeft = false;			//need left value of the expression, if it is true, the expression will generate left value, else only generate right value
	private int compoundLevel = 0;
	
	private GenerationVisitor(GenerationVisitor generationVisitor) {
		this.level = generationVisitor.level;
		this.displacement = generationVisitor.displacement;
		this.logPrinter = generationVisitor.logPrinter;
		this.codeTable = generationVisitor.codeTable;
		this.spellingTable = generationVisitor.spellingTable;
		this.funSize = generationVisitor.funSize;
		this.loopBeginLab = generationVisitor.loopBeginLab;
		this.loopEndLab = generationVisitor.loopEndLab;
		this.switchDefaultLab = generationVisitor.switchDefaultLab;
		this.switchItem = generationVisitor.switchItem;
		this.switchEndLab = generationVisitor.switchEndLab;
		this.needLeft = generationVisitor.needLeft;
		this.compoundLevel = generationVisitor.compoundLevel;
	}

	private GenerationVisitor(int level, int compoundLevel ,  Recorder recorder , GenerationVisitor v) {
		this.level = level;
		this.displacement = recorder;
		this.compoundLevel = compoundLevel;
		
		this.logPrinter = v.logPrinter;
		this.codeTable = v.codeTable;
		this.spellingTable = v.spellingTable;
		this.funSize = v.funSize;
		this.loopBeginLab = v.loopBeginLab;
		this.loopEndLab = v.loopEndLab;
		this.switchDefaultLab = v.switchDefaultLab;
		this.switchItem = v.switchItem;
		this.switchEndLab = v.switchEndLab;
		this.needLeft = v.needLeft;
	}

	public GenerationVisitor(CodeTable table, LogPrinter printer, ArrayList<String> spellingTable) {
		this.codeTable = table;
		this.logPrinter = printer;
		this.spellingTable = spellingTable;
	}
	
	/**
	 * print info of entering function to log
	 * @param node
	 */
	private void printEnterInfo( ASTNode node ){
		logPrinter.println( String.format( "%d : Entering %s  ", node.getLineNum() , node.getClass().getName() ) ) ;
	}
	
	/**
	 * print info of leaving function to log
	 * @param node
	 */
	private void printLeaveInfo( ASTNode node ){
		logPrinter.println( String.format( "%d : Leving %s  ", node.getLineNum() , node.getClass().getName() ) ) ;
	}
	
	/**
	 * return a string which form is ( l , d )
	 */
	private String pair( int l, int d ){
		return "(" + l + "," + d + ")";
	}
	
	/**
	 * for function, we need use a function visitor to visit it.
	 * this new visitor's level is  "current level + 1", and it has a new displacement recorder which begins mallocing from 2 since 0 is link field and 1 is return address
	 * @return
	 */
	private Visitor functionVisitor(){
		return new GenerationVisitor(level+1, compoundLevel, new Recorder(2), this );
	}
	
	/**
	 * for function, we need use a compound  visitor to visit it.
	 * this new visitor's level is  "current level + 1", and it has a new displacement recorder which begins mallocing from 1 since 0 is link field
	 * @return
	 */
	private Visitor compoundVisitor(){
		return new GenerationVisitor(level+1, compoundLevel+1, new Recorder(1) , this );
	}
	
	/**
	 * malloc space whose size is given by user
	 * @param space
	 * @return
	 */
	public String mallocSpace( int space ){
		int base = displacement.getValue();
		displacement.setValue( base + space );
		return pair( level, base );
	}
	
	/**
	 * malloc temporary
	 * @return
	 */
	private String getTemp(){
		int t = temp;
		temp++;
		return "t"+t;
	}
	
	/**
	 * is the operand can be determined in compiling
	 * @param operand
	 * @return
	 */
	private boolean isDetermined( String operand ){
		try{
			Integer.parseInt( operand );
			return true;
		}catch( Exception e ){
			return false;
		}
	}
	
	/**
	 * are the two operands can be determined in compiling
	 * @param operand1
	 * @param operand2
	 * @return
	 */
	private boolean canDetermined( String operand1, String operand2 ){
		return isDetermined(operand1) && isDetermined(operand2);
	}
	
	/**
	 * malloc a new lab
	 * @return
	 */
	private String mallocLab(){
		return "L"+ lab++;
	}
	
	/**
	 * product quadruple into code table
	 * @param operator
	 * @param address1
	 * @param address2
	 * @param result
	 */
	private void productCode( String operator, String address1, String address2 , String result ){	
		logPrinter.println( String.format( "product %s, %s , %s , %s ", operator , address1 , address2 , result));
		if ( codeTable.add( operator, address1, address2, result ) ) 
			temp--;
	}
	
	/**
	 * translate from TokenType into quadruple operator
	 * @param operator
	 * @return
	 */
	private String translate( TokenType operator ){
		switch ( operator ) {
		case LTEQ :
			return "lte";
		case LT:
			return "lt";
		case GT:
			return "gt";
		case GTEQ:
			return "gte";
		case EQ:
			return "eq";
		case NEQ:
			return "neq";
		case PLUS:
			return "add";
		case MINUS:
			return "sub";
		case OR:
			return "or";
		case MULT:
			return "mul";
		case DIV:
			return "div";
		case MOD:
			return "mod";
		case AND:
			return "and";
		case ANDTHEN:
			return "and";
		case ORELSE:
			return "or";
		default:
			return "";
		}
	}

	@Override
	public void visit(ASTNode node) {
		printEnterInfo(node);
		if( node instanceof VariableDeclarationNode ){
			visitVariableDeclarationNode( (VariableDeclarationNode) node );
		}else if( node instanceof FunctionDeclarationNode ){
			visitFunctionDeclarationNode( (FunctionDeclarationNode) node );
		}else if( node instanceof LiteralNode ){
			visitLiteralNode( (LiteralNode) node );
		}else if( node instanceof BinaryOperationNode ){
			visitBinaryOperationNode( (BinaryOperationNode) node );
		}else if( node instanceof UnitaryOperationNode ){
			visitUnitaryOperationNode( (UnitaryOperationNode) node );
		}else if( node instanceof VariableNode ){
			visitVariableNode( (VariableNode) node );
		}else if( node instanceof CallNode ){
			visitCallNode( (CallNode) node );
		}else if( node instanceof BranchStmtNode ){
			visitBranchStmtNode( (BranchStmtNode) node );
		}else if( node instanceof CompoundStmtNode ){
			visitComspoundStmtNode( (CompoundStmtNode) node );
		}else if( node instanceof MarkerStmtNode ){
			visitMarkerStmtNode( (MarkerStmtNode) node );
		}else if( node instanceof ReturnStmtNode ){
			visitReturnStmtNode( (ReturnStmtNode) node );
		}else if( node instanceof IfStmtNode ){
			visitIfStmtNode( (IfStmtNode) node );
		}else if( node instanceof LoopStmtNode ){
			visitLoopStmtNode( (LoopStmtNode) node );
		}else if( node instanceof CaseNode ){
			visitCaseNode( (CaseNode) node );
		}else if( node instanceof ParameterNode ){
			visitParameterNode( (ParameterNode) node );
		}else if( node instanceof AssignStmtNode ){
			visitAssignStmtNode( (AssignStmtNode) node );
		}
		printLeaveInfo(node);
	}
	
	public 	GenerationVisitor newVisitor(){
		return new GenerationVisitor( this );
	}
	
	@Override
	public void visitAssignStmtNode(AssignStmtNode node) {
		VariableNode leftVar = node.getLeftValue();
		ExpressionNode rightExp = node.getRightvalue();
		//traversal left variable and right expression
		needLeft = true;
		leftVar.accept( newVisitor() );		
		needLeft = false;
		if( leftVar.isArray() ){
			//if left variable is array, we product "tae" code
			//note: it will have a index, this is promised by semantic analyzer
			rightExp.accept( newVisitor() );
			productCode("tae", rightExp.getResult() , leftVar.getIndex().getResult() , leftVar.getArrayBase() );
		}else{		
			//if right variable is variable, we product "asg" code
			rightExp.accept( newVisitor() );
			productCode("asg", rightExp.getResult()  , "", leftVar.getResult() );
		}
	}

	@Override
	public void visitCaseNode(CaseNode node) {
		//get current case lab, and product it( Note: current case lab is provided by last case node )
		productCode( "lab" , "" , "", switchCurrentLab );
		if( node.getNumber()!=null ){
			//represent: this node is not 'default' case
			//Note: if the node is default case, it won't product following codes
			//Note: default node will be the last case node. if it isn't, then branch node will replace it to the last
			String temp = getTemp();
			String nextLab = mallocLab();		
			productCode("eq", switchItem, node.getNumber().toString() , temp );
			productCode("iff", temp, "", nextLab );
			//let next case use this as its lab, then "iff" can jump to it
			switchCurrentLab = nextLab;			
		}
		//traversal statement
		node.getStmt().accept( newVisitor() );
		productCode("goto", "" , "" , switchEndLab );
	}

	@Override
	public void visitLoopStmtNode(LoopStmtNode node) {
		loopBeginLab = mallocLab();
		loopEndLab = mallocLab();
		productCode( "lab", "", "",  loopBeginLab );
		ArrayList<StmtNode> stmts = node.getStatements();
		for( int i = 0 ;i < stmts.size(); i++ ){
			stmts.get(i).accept( newVisitor() );
		}
		productCode( "goto" ,"" , "" , loopBeginLab );
		productCode( "lab" , "" , "" ,  loopEndLab );
	}

	@Override
	public void visitIfStmtNode(IfStmtNode node) {
		//traversal condition
		ExpressionNode condition = node.getCondition();
		condition.accept( newVisitor() );
		
		String endLab = mallocLab();
		StmtNode s = node.getElseBody();
		if(s==null){
			/*
			 * represent ifNode does not have a else branch
			 * so we will jump to endLab when condition does not match
			 */
			productCode("iff", condition.getResult() ,"",""+endLab);
			node.getTrueBody().accept( newVisitor() );
			productCode("lab", "" ,"",endLab);
		}
		else{
			/*
			 * represent elseNode has a else branch
			 * so we will jump to elseLab when condition does not match
			 * and when 'if' and 'else' branch finished, we will jump to endLab
			 */
			String elseLab = mallocLab();
			productCode("iff", condition.getResult() ,"",""+elseLab);
			node.getTrueBody().accept( newVisitor() );
			productCode("goto", "", "", endLab);
			productCode("lab", "", "", elseLab );
			node.getElseBody().accept( newVisitor() );
			//note: In 'else' case, we don't need to jump, since it will flow down to endLab automatically
			productCode("lab", "" , "" , endLab );
		}		
	}
	
	@Override
	public void visitReturnStmtNode(ReturnStmtNode node) {
		for( int i=compoundLevel; i>0; i-- ){
			productCode("lcs", "", "", "");
		}
		
		if( node.getValue()!=null ){
			//represent return stmt has a value and declaration of this function will have a non void type( It's promised by semantic analyzer )
			node.getValue().accept( newVisitor());
			productCode("retv", String.valueOf(funSize) , node.getValue().getResult() ,"");
		}else{
			//represent return stmt has a value and declaration of this function will have a void type( It's promised by semantic analyzer )
			productCode("ret", String.valueOf(funSize) , "" ,"");
		}
	}

	@Override
	public void visitMarkerStmtNode(MarkerStmtNode node) {
		switch( node.getType() ){
		case CONTINUE:
			productCode("goto", "","", loopBeginLab);
			return;
		case EXIT:
			for( int i=compoundLevel -1; i>=0 ; i-- ){
				productCode("lcs", "", "", "");
			}
			productCode("goto", "","", loopEndLab);
			return;
		}
	}

	@Override
	public void visitComspoundStmtNode(CompoundStmtNode node) {
		//provide a new compound visitor
		Visitor v = compoundVisitor();
		//get local declaration size
		int size = 0;
		ArrayList<DeclarationNode> dec = node.getDeclarations();
		for( int i=0 ; i<dec.size(); i++ ){
			dec.get(i).accept( v );
			size += ((VariableDeclarationNode)dec.get(i)).getTotalSizes();
		}
		if( size>0 ){
			//product "ecs"
			productCode("ecs", String.valueOf( size ) ,"", "");	
		}else{
			v=newVisitor();
		}
		//traversal statements
		ArrayList<StmtNode> stmts = node.getStmtNode();
		for( int i=0 ; i<stmts.size(); i++ ){
			stmts.get(i).accept( v );
		}
		if( size>0 ){
			//product "lcs"
			productCode("lcs","","","");
		}		
	}

	@Override
	public void visitBranchStmtNode(BranchStmtNode node) {
		//malloc switch end lab
		switchEndLab = mallocLab();
		//traversal add-exp and get it's result
		node.getExp().accept( newVisitor() );
		switchItem = node.getExp().getResult();
		ArrayList<CaseNode> cases  = node.getCases();
		//initial first case's lab
		switchCurrentLab = mallocLab();
		//find defaultNode and traveral other node at same time
		CaseNode defaultNode = null;
		for( int i=0 ; i<cases.size(); i++ ){
			CaseNode caseNode = cases.get(i);
			if( caseNode.getNumber()== null ){
				//there is a default node, record default node
				defaultNode = caseNode;
				continue;
			}
			caseNode.accept(newVisitor());
		}
		if( defaultNode !=null ){
			//traversal default node at last
			defaultNode.accept( newVisitor() );
		}else{
			//errors
			productCode("lab", "", "", switchCurrentLab );
			//productCode("wrc", "e", "", "" );
		}		
		productCode("lab", "", "", switchEndLab );
	}

	@Override
	public void visitCallNode(CallNode node) {
		//replace environment functions' codes 
		String lexeme = spellingTable.get( node.getIdentifier() );
		if( lexeme.equals("readint") ){
			String temp = getTemp();
			productCode("rdi", "", "", temp);
			node.setResult( temp );
			return;
		}else if( lexeme.equals("readbool") ){
			String temp = getTemp();
			productCode("rdb", "", "", temp);
			node.setResult( temp );
			return;
		}else if( lexeme.equals("writeint") ){
			node.getArguements().get(0).accept( newVisitor() );
			String arg = node.getArguements().get(0).getResult();
			productCode("wri", arg, "", "");
			return;
		}else if( lexeme.equals("writebool" )){
			node.getArguements().get(0).accept( newVisitor() );
			String arg = node.getArguements().get(0).getResult();
			productCode("wrb", arg, "", "");
			return;
		}
		
		//malloc return value space
		String temp = getTemp();
		productCode("rval", "", "", temp);
		node.setResult(temp);
		//push arguments from right to left
		ArrayList<ExpressionNode> arguements = node.getArguements();
		for( int i=arguements.size()-1 ; i>=0; i-- ){
			arguements.get(i).accept( newVisitor() );
		}
		for( int i=arguements.size()-1 ; i>=0; i-- ){
			ExpressionNode arg = arguements.get(i);
//			arg.accept( newVisitor() );
			if( arg instanceof VariableNode ){
				VariableNode var = (VariableNode)arg;
				ParameterNode param = ((FunctionDeclarationNode)node.getDeclaration()).getParameterList().get(i);
				if( param.isRef() ){
					productCode("arga", var.getResult() , "" , "");
				}else if( param.isArray() ){
					productCode( "arga", var.getArrayBase(), "", "");
				}else{
					productCode("arg", var.getResult(), "", "");
				}
			}else{
				productCode("arg", arg.getResult() , "", "");
			}
		}
		productCode("call", spellingTable.get( node.getIdentifier() ), "", "");
	}

	@Override
	public void visitVariableNode(VariableNode node) {
		DeclarationNode dec =node.getDeclaration();
		if( node.isArray() ){
			//variable is an array
			//Note: 
			//		Although the node represent an array, but it may does not have index, since
			//			void fun( int a[]);
			//			fun(a);			
			//set array base
			if( dec instanceof ParameterNode ){	
				//if it's declared in param, it's level must be 1, displacement must be negative
				int d = -((ParameterNode) dec).getSequeNum();
				node.setArrayBase( pair(1,d) );
			}else{
				String arrayBase = ( (VariableDeclarationNode) dec ).getPosition();
				node.setArrayBase(arrayBase);				
			}			
			
			//note: when pass name of array, node doesn't have index
			if( node.getIndex()!=null ){
				boolean needL =  needLeft;
				needLeft = false;
				//traversal index node and set index value
				node.getIndex().accept( newVisitor() );
				if( needL == false ){
					//present we just need its right value
					String result =getTemp();
					productCode("fae", node.getArrayBase() , node.getIndex().getResult() , result);
					//set result
					node.setResult(result);
				}				
			}
		}else{
			//variable is not an array
			if( dec instanceof ParameterNode ){	
				//if it's declared in param, it's level must be 1, displacement must be negative
				int d = -((ParameterNode) dec).getSequeNum();
				node.setResult( pair(1,d) );
			}else{
				String result = ( (VariableDeclarationNode) dec ).getPosition();
				node.setResult(result);				
			}			
		}
	}

	@Override
	public void visitUnitaryOperationNode(UnitaryOperationNode node) {
		//traversal operand's node
		ExpressionNode operand = node.getOperand();
		operand.accept( newVisitor() );
		
		if( isDetermined( operand.getResult() )){
			//if the value can be determined in code generation, compute it now
			String result = compute( node.getOperator()  ,operand.getResult() );
			node.setResult(result);
		}else{
			//if the value can not be determined in code generation, product code
			//convert to quadruple operator from TokenType
			String operator = "";
			if( node.getOperator()==TokenType.MINUS ){
				operator = "uminus";
			}else{
				operator = "not";
			}
			//product code
			String temp = getTemp();
			productCode(operator, operand.getResult() , "", temp);
			node.setResult(temp);
		}
	}

	private String compute( TokenType operator, String operand ) {
		int op = Integer.parseInt(operand);
		switch( operator ){
		case  MINUS:
			return String.valueOf(-op);
		case NOT:
			return String.valueOf( op==0?1:0 );		
		}
		return null;		
	}

	@Override
	public void visitBinaryOperationNode(BinaryOperationNode node) {
		//check if this binary operation is short circulated
		if( node.getOperator()== TokenType.ANDTHEN || node.getOperator()==TokenType.ORELSE ){
			//malloc two label
			String circulatedLab = mallocLab();
			String endLab = mallocLab();
			//traversal left exp 
			node.getLeftExp().accept( newVisitor() );
			if( isDetermined( node.getLeftExp().getResult() )){
				//represent left exp can be determined when compiling
				//it has following conditions:
				//false&&(a+b)<=>false
				//true&&(a+b)<=>a+b
				//true||( a+b)<=>true
				//false||(a+b)<=>a+b
				if( node.getLeftExp().getResult().equals("0") && node.getOperator() == TokenType.ANDTHEN ){
					//false&&(a+b)<=>It is always false no matter what is follow false
					node.setResult("0");
				}else if(!node.getLeftExp().getResult().equals("0") && node.getOperator() == TokenType.ORELSE){
					//true||( a+b)<=>It is always true no matter what is follow ORElSE
					node.setResult("1");
				}else{
					//represent the result depend on second operand
					node.getRightExp().accept(newVisitor());
					node.setResult( node.getRightExp().getResult() );
				}
			}else{
				//represent left operand can not be determined when compiling
				//product code which represent if the first result can determine final result, ignore right operand
				productCode( node.getOperator()==TokenType.ANDTHEN? "iff": "ift", node.getLeftExp().getResult(), "", circulatedLab);
				//traversal right operand
				node.getRightExp().accept( newVisitor() );				
				if( isDetermined( node.getRightExp().getResult() )){
					//represent right operand can be determined when compiling
					//it has following conditions:
					//(a>b)&&true <=> a>b
					//(a>b)&&false <=> false
					//(a>b)||true <=> true
					//(a>b)||false <=> a>b
					//we won't need last "iff" or "ift"
					codeTable.pop();
					if( node.getRightExp().getResult().equals("0")){
						//(a>b)&&false <=> false
						//(a>b)||false <=> a>b
						node.setResult( node.getOperator()==TokenType.ANDTHEN? "0": node.getLeftExp().getResult() );
					}else{
						//(a>b)&&true <=> a>b
						//(a>b)||true <=> true
						 node.setResult( node.getOperator()==TokenType.ANDTHEN? node.getLeftExp().getResult(): "1");
					}
				}else{
					if( node.getRightExp().getResult().contains("t") ){
						//represent right expression's result is temporary
						node.setResult( node.getRightExp().getResult() );
					}else{
						//represent right expression's result is not temporary, we copy it to another temporary
						//since the value may be changed by next steps
						String temp = getTemp();
						productCode("asg", node.getRightExp().getResult()  , "", temp);
						node.setResult( temp );
					}					
					//jmp over 'asg' instruction
					productCode("goto", "", "", endLab );
					productCode("lab", "", "", circulatedLab );
					productCode("asg", node.getOperator()==TokenType.ANDTHEN?"0":"1", "", node.getRightExp().getResult() );
					productCode("lab", "", "", endLab);
				}
			}
			return;
		}

		//traversal operands' nodes
		node.getLeftExp().accept( newVisitor() );
		node.getRightExp().accept( newVisitor() );
		if( canDetermined( node.getLeftExp().getResult() , node.getRightExp().getResult() )){
			String result = compute( node.getOperator()  ,node.getLeftExp().getResult() , node.getRightExp().getResult() );
			node.setResult( result );
		}else {			
			//product code
			String operator = translate( node.getOperator() );
			String temp = getTemp();				
			productCode(operator, node.getLeftExp().getResult() , node.getRightExp().getResult() , temp);
			node.setResult( temp );
		}
	}

	/**
	 * compute operands by operator in compiling
	 * @param operator
	 * @param oprand1
	 * @param oprand2
	 * @return
	 */
	private String compute( TokenType operator, String oprand1, String oprand2) {
		int op1 = Integer.parseInt( oprand1 );
		int op2 = Integer.parseInt( oprand2 );
		int result = 0;
		switch ( operator ) {
		case LTEQ :
			result = op1<=op2? 1 : 0;
			break;
		case LT:
			result = op1<op2? 1 : 0;
			break;
		case GT:
			result = op1>op2? 1 : 0;
			break;
		case GTEQ:
			result = op1>=op2? 1 : 0;
			break;
		case EQ:
			result = op1==op2? 1 : 0;
			break;
		case NEQ:
			result = op1!=op2? 1 : 0;
			break;
		case PLUS:
			result = op1+op2;
			break;
		case MINUS:
			result = op1-op2;
			break;
		case OR:
			result = op1!=0 || op2!=0 ? 1 : 0;
			break;
		case MULT:
			result = op1*op2;
			break;
		case DIV:
			result = op1/op2;
			break;
		case MOD:
			result = op1%op2;
			break;
		case AND:
			result = op1!=0 && op2!=0 ? 1 : 0;
			break;
		default:
			return "";
		}
		return String.valueOf(result);
	}


	@Override
	public void visitLiteralNode(LiteralNode node) {
		node.setResult( String.valueOf( node.getValue() ) );
	}

	@Override
	public void visitVariableDeclarationNode(VariableDeclarationNode node) {
		if( node.getSize()==null ){
			//It is not an array declaration
			node.setPosition( mallocSpace(1) );
			node.setSizeValue(1);
		}else{
			//It is an array declaration
			//traversal size
			node.getSize().accept( newVisitor() );
			String position = mallocSpace( Integer.valueOf( node.getSize().getResult() ));
			node.setPosition(position);
			//Note: size will be a literal integer, since
			//		1.It's type must be INT which is promised by semantic analyzer
			//		2.It's value must be determined which is promised by semantic analyzer
			//		3.It's value will be compute to a literal integer which is designed in this phrase for operation whose value is determined in compiling time
			node.setSizeValue( Integer.valueOf( node.getSize().getResult() ));
		}
		//traversal follow declaration
		ArrayList<VariableDeclarationNode> arrayLists = node.getFollowList();
		for(int i=0; i<arrayLists.size(); i++ ){
			arrayLists.get(i).accept( newVisitor() );
		}
	}

	@Override
	public void visitFunctionDeclarationNode(FunctionDeclarationNode node) {
		//ignore environment functions
		String lexeme = spellingTable.get( node.getIdentifier() );
		if( lexeme.equals("readint") ||  lexeme.equals("readbool") || lexeme.equals("writeint") || lexeme.equals("writebool") ){
			return;
	}
		
		ArrayList<ParameterNode> params = node.getParameterList();
		//set sequence number of parameters 
		for( int i=0; i<params.size(); i++ ){
			params.get(i).setSequeNum(i+1);
		}
		//set function return size, which is needed by return statement
		funSize = params.size();
		
		//get local declaration size
		int size = 0;
		ArrayList<DeclarationNode> decs = node.getComspond().getDeclarations();
		ArrayList<StmtNode> stmts = node.getComspond().getStmtNode();
		Visitor v = functionVisitor();
		for(int i=0; i<decs.size(); i++ ){
			VariableDeclarationNode dec = (VariableDeclarationNode) decs.get(i);
			dec.accept( v );
			size += dec.getTotalSizes();
		}
		//product code
		productCode("fun", lexeme, String.valueOf( size ), "");
		
		//traversal stmts
		for(int i=0; i<stmts.size(); i++ ){
			stmts.get(i).accept( v );
		}
		
		if(node.getType()==TokenType.VOID && !( stmts.get( stmts.size()-1 ) instanceof ReturnStmtNode ) ){
			productCode("ret", String.valueOf( funSize ) , "" , "");
		}	
	}
	
	@Override
	public void visitParameterNode(ParameterNode node) {}
}
